package com.demo.seckill.web;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.demo.seckill.entity.SuccessKilled;
import com.demo.seckill.enums.SeckillStatEnum;
import com.demo.seckill.service.ISeckillService;
import com.demo.seckill.service.ISuccessKilledService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.*;

/**
 * <p>
 * 秒杀库存表 前端控制器
 * </p>
 *
 * @author 翌擎科技
 * @since 2021-10-26
 * 模拟多线程秒杀案例
 */
@Api(tags = "秒杀")
@RestController
@RequestMapping("/seckill/seckill")
@Slf4j
public class SeckillController {

    private static int corePoolSize = Runtime.getRuntime().availableProcessors();
    /**
     * 空闲线程存活时间
     */
    private static long keepAliveTime = 10L;

    /**
     * 创建线程池  调整队列数 拒绝服务
     */
    private static ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(corePoolSize, corePoolSize + 1, keepAliveTime, TimeUnit.SECONDS
            //线程等待队列
            , new LinkedBlockingQueue<>(10000)
            //默认线程工厂
            , Executors.defaultThreadFactory()
            //线程池拒绝策略
            , new ThreadPoolExecutor.AbortPolicy());


    @Autowired
    private ISeckillService seckillService;
    @Autowired
    private ISuccessKilledService successKilledService;

    /**
     * 校验库存->库存大于0->扣减库存->新建订单->支付
     * 出现超卖
     *
     * @param seckillId 秒杀Id
     */
    @ApiOperation("秒杀一(最low实现)")
    @RequestMapping(value = "/start/one", method = RequestMethod.GET)
    public void startOne(long seckillId) {
        CountDownLatch latch = new CountDownLatch(100);
        log.info("秒杀开始：Id={}", seckillId);
        for (int i = 1; i <= 100; i++) {
            long userId = i;
            Runnable task = () -> {
                try {
                    SeckillStatEnum statEnum = seckillService.startOneSeckill(seckillId, userId);
                    if (statEnum != null) {
                        log.info("用户userId={},秒杀结果={}", userId, statEnum.getInfo());
                    } else {
                        log.info("用户:{}{}", userId, "哎呦喂，人也太多了，请稍后！");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.info("服务器错误！");
                }
                latch.countDown();
            };
            poolExecutor.execute(task);
        }

        try {
            latch.await();

            int count = successKilledService.count(new LambdaQueryWrapper<SuccessKilled>()
                    .eq(SuccessKilled::getSeckillId, seckillId));
            log.info("活动{}一共秒杀商品{}件", seckillId, count);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 加锁（公平锁）-> 校验库存->库存大于0->扣减库存->新建订单->支付->释放锁
     * 程序锁
     *
     * @param seckillId 秒杀Id
     */
    @ApiOperation("秒杀e二(程序锁)")
    @RequestMapping(value = "/start/two", method = RequestMethod.GET)
    public void startTwo(long seckillId) {
        CountDownLatch latch = new CountDownLatch(100);
        log.info("秒杀开始：Id={}", seckillId);
        for (int i = 1; i <= 5000; i++) {
            long userId = i;
            Runnable task = () -> {
                try {
                    SeckillStatEnum statEnum = seckillService.startTwoSeckill(seckillId, userId);

                    if (statEnum != null) {
                        log.info("用户userId={},秒杀结果={}", userId, statEnum.getInfo());
                    } else {
                        log.info("用户:{}{}", userId, "哎呦喂，人也太多了，请稍后！");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.info("服务器错误！");
                }
                latch.countDown();
            };
            poolExecutor.execute(task);
        }

        try {
            latch.await();
            int count = successKilledService.count(new LambdaQueryWrapper<SuccessKilled>()
                    .eq(SuccessKilled::getSeckillId, seckillId));
            log.info("活动{}一共秒杀商品{}件", seckillId, count);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 加锁（公平锁）-> 校验库存->库存大于0->扣减库存->新建订单->支付->释放锁
     * 程序锁
     *
     * @param seckillId 秒杀Id
     */
    @ApiOperation("秒杀三(AOP锁)")
    @RequestMapping(value = "/start/three", method = RequestMethod.GET)
    public void startThree(long seckillId) {
        CountDownLatch latch = new CountDownLatch(100);
        log.info("秒杀开始：Id={}", seckillId);
        for (int i = 1; i <= 5000; i++) {
            long userId = i;
            Runnable task = () -> {
                try {
                    SeckillStatEnum statEnum = seckillService.startThreeSeckill(seckillId, userId);
                    if (statEnum != null) {
                        log.info("用户userId={},秒杀结果={}", userId, statEnum.getInfo());
                    } else {
                        log.info("用户:{}{}", userId, "哎呦喂，人也太多了，请稍后！");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.info("服务器错误！");
                }
                latch.countDown();
            };
            poolExecutor.execute(task);
        }
        try {
            latch.await();
            int count = successKilledService.count(new LambdaQueryWrapper<SuccessKilled>()
                    .eq(SuccessKilled::getSeckillId, seckillId));
            log.info("活动{}一共秒杀商品{}件", seckillId, count);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 加锁（悲观锁）-> 校验库存->库存大于0->扣减库存->新建订单->支付->释放锁
     * 悲观锁
     *
     * @param seckillId 秒杀Id
     */
    @ApiOperation("秒杀四(悲观锁)")
    @RequestMapping(value = "/start/four", method = RequestMethod.GET)
    public void startFour(long seckillId) {
        CountDownLatch latch = new CountDownLatch(100);
        log.info("秒杀开始：Id={}", seckillId);
        for (int i = 1; i <= 5000; i++) {
            long userId = i;
            Runnable task = () -> {
                try {
                    SeckillStatEnum statEnum = seckillService.startFourSeckill(seckillId, userId);
                    if (statEnum != null) {
                        log.info("用户userId={},秒杀结果={}", userId, statEnum.getInfo());
                    } else {
                        log.info("用户:{}{}", userId, "哎呦喂，人也太多了，请稍后！");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.info("服务器错误！");
                }
                latch.countDown();
            };
            poolExecutor.execute(task);
        }
        try {
            latch.await();
            int count = successKilledService.count(new LambdaQueryWrapper<SuccessKilled>()
                    .eq(SuccessKilled::getSeckillId, seckillId));
            log.info("活动{}一共秒杀商品{}件", seckillId, count);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 校验库存->库存大于0->扣减库存->新建订单->支付
     * 悲观锁
     *
     * @param seckillId 秒杀Id
     */
    @ApiOperation("秒杀五(数据库乐观锁)")
    @RequestMapping(value = "/start/five", method = RequestMethod.GET)
    public void startFive(long seckillId) {
        CountDownLatch latch = new CountDownLatch(100);
        log.info("秒杀开始：Id={}", seckillId);
        for (int i = 1; i <= 5000; i++) {
            long userId = i;
            Runnable task = () -> {
                try {
                    //这里使用的乐观锁、可以自定义抢购数量、如果配置的抢购人数比较少、比如120:100(人数:商品) 会出现少买的情况
                    //用户同时进入会出现更新失败的情况
                    SeckillStatEnum statEnum = seckillService.startFiveSeckill(seckillId, userId);
                    if (statEnum != null) {
                        log.info("用户userId={},秒杀结果={}", userId, statEnum.getInfo());
                    } else {
                        log.info("用户:{}{}", userId, "哎呦喂，人也太多了，请稍后！");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.info("服务器错误！");
                }
                latch.countDown();
            };
            poolExecutor.execute(task);
        }
        try {
            latch.await();
            int count = successKilledService.count(new LambdaQueryWrapper<SuccessKilled>()
                    .eq(SuccessKilled::getSeckillId, seckillId));
            log.info("活动{}一共秒杀商品{}件", seckillId, count);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
